/*
    YmsgIRC - Program enabling YMSG chat clients to use IRC
    Copyright (C) 2011 Crash Daemonicus ( crashenator -at- gmail.com )

    This program is free software: you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/
/**
@file network.h
@brief Provides a base for an Instant Messaging network implementation.
@details This library provides easy to use connection support (NwConnection),
a basic model for implementing Instant Messaging networks (NwNetwork)
and intercomponent messaging support through use of event and action message (NwNotify) queues.
@author crashenator (crashenator -at- gmail.com)
*/


#pragma once

#include "common.h"
#include "sockets_cross.h"
#include "channel.h"
#include <queue>

#define NW_RESERVED_USERNAME "YmsgIRC"///< Reserved username for user-program interaction.
#define NW_RESERVED_CHANPREFIX '#'

/// Program states for use with NwNetwork::step()
enum NwStepState
{
    NW_STEP,///< Indicates program loop iteration.
    NW_STEP_BEGIN,///< Indicates program/networking startup.
    NW_STEP_END,///< Indicates program/networking shutdown.
};

/** @internal
socket blocking states (NwConnection::blocked)
*/
enum NwBlockState
{
    NW_BLOCKED_UNKNOWN,
    NW_UNBLOCKED,
    NW_BLOCKED
};

/**
@brief Name type distinctions
@details States specifying the type of Name string for distinction of Username/Channel/Reserved names.\n
These describe *what* a name (origin, destination) in a message/event actually *is*.\n
These are used to communicate meaning both internally and when messages are read outside of their Network class
as well as serving as Return Values for validation functions. (NwNetwork::CheckOrigin())

Example: If an instant message is sent to "X", X could be a chatroom (send chat post), a username (send IM),
your username (relay back), a reserved username (relay program options), or invalid (do nothing/error).\n
These states allow conditional code like this which is especially useful when the `Name` changes depending on
program/component context so that meaning won't be lost.
*/
enum NwCheckType
{
    NW_CHECK_INVALID,///< not used, invalid name state (0)
    NW_CHECK_UNKNOWN,///< the Name
    NW_CHECK_USERNAME,///< the Name is the current Network Username
    NW_CHECK_CHANNEL,///< the Name is the current Network Channel
    NW_CHECK_CHANNEL_UNKNOWN,///< the Name is a channel
    NW_CHECK_RESERVED,///< the Name is the Reserved Username (NW_RESERVED_USERNAME)
    NW_CHECK_BLANK  ///< the Name is blank (easy enough to check)
};

/**
@brief Event and Action notification types
@details These types are an abstraction of common Instant-Messaging network abilities
Allowing Network implementations to notify the program of Events and Perform actions
using these like-terms allows easier design, portability, reuse and even intercommunication.
*/
enum NwNotifyType
{
    NW_NONE,
    NW_CONNINFO,
    NW_CONNECT,
    NW_DISCONNECT,
    NW_LOGIN,
    NW_LOGOUT,
    NW_USERNAME,
    NW_PASSWORD,
    NW_ULIST_START,
    NW_ULIST_NAME,
    NW_ULIST_TOPIC,
    NW_ULIST_APPEND,
    NW_ULIST_REMOVE,
    NW_ULIST_END,
    NW_PRIVMSG,
    NW_CHATMSG,
    NW_DATA
};

/* TODO (#1#): LoggedIn/InChat  notifications and Network state 
               variables? */

/* TODO (#1#):  NW_ONLINE notifications? (IRC->E:LOGIN ->  
               A:ONLINE(NickServ,...)->YMSG) */



string NwNotifyType_Debug(NwNotifyType t);

class NwNotify
{
    public:
        NwNotifyType typ;
        string      sDat[3];
        NwCheckType cDat[3];
        int         iDat[3];
    
        NwNotify(NwNotifyType type);
        NwNotify(NwNotifyType type,string s1,string s2,string s3);
        const NwNotify& sSet(NwNotifyType type,string s1,string s2,string s3);
};



class NwConnection
{
    public:
        string hostname;
        string ip;
        unsigned short port;
        SOCKET sock;
        sockaddr_in sin;
        NwBlockState blocked;
        int error;
        char buf[0x10000];
        int ixBuf;//last byte inside buffer limits
        int iwBuf;//next byte to be written at in buffer (inside limits)
        bool buf_nullterminated;//null the byte following the last byte written by Get
        void setBufLimit(size_t length);//length of the buffer allowed to be written on by Get
        size_t getBufLimit();//length of the buffer allowed to be written on by Get
        size_t lAvail(size_t bytes=0) const;
        int iReset(bool conditional=false);
        int iForward(size_t bytes,bool limit=false);
        int iLastWritten() const;
        int block(bool state);
        int  get(size_t bytes,bool blocking=false);
        bool put(const char* buf,size_t bytes=0,bool blocking=true);
        bool put(const string& buf,size_t bytes=0,bool blocking=true);
        bool set(const string& host,unsigned short uPort);
        bool open();
        bool close();
        bool listn(int iPending=-1);//can't be named Listen
        bool acceptFrom(const SOCKET& sockListen);
        bool acceptFrom(const NwConnection& listener);
        NwConnection();
};

class NwNetwork
{
    public:
        string name;
        queue<NwNotify> events;
        queue<NwNotify> actions;
        NwConnection connection;
        //Channel buddylist;//not supported at this time - could be supported the same way as ULIST notifications
        Channel channel;
        string username;
        string password;   
        
        
        NwCheckType CheckOrigin(string u,bool defblank=true);
        
        //NwA, Action functions do a default Action; NwE, Event functions Notify of an existing Event.
        void NwAConnInfo(const string& host, unsigned int port);
        void NwEAccept(const NwConnection& listener, bool conditionalEvt=true);
        void NwEConnect(bool conditionalEvt=true);
        void NwEDisconnect(bool conditionalEvt=true);
        void NwEUsername(string from,string to);
        void NwEUsername(string to);
        void NwEChanName(string c);
        void NwEChanTopic(string c, string t);
        void NwEChanStart(string c, string t);
        bool NwEChanAppend(string c, string u);
        bool NwEChanRemove(string c, string u);
        void NwEChanReplace(string c, string from, string to);
        void NwEChanEnd(string c);
        const NwNotify& NwEMsg(string src,string dst, string txt,int info=0);
        
        
        virtual void stepActions();//things this class is told to do
        virtual void stepRecv();//network transfer data / processing
        virtual void step(NwStepState state=NW_STEP);//called to generically step the class
        virtual void notifyStateText(const string& state);

		
        
};
